home *** CD-ROM | disk | FTP | other *** search
Text File | 1992-11-23 | 16.2 KB | 658 lines | [TEXT/PJMM] |
- unit Tasks;
-
- { Task Manager -- Background processing support}
- { version 2.2.1}
-
- { This software source package is Copyright © 1990-91 by Michael Hecht. All Rights}
- { Reserved. It may be freely distributed in source or object code format; however,}
- { the source code may not be sold for profit or charged for in any way. The source}
- { code must be distributed as a package including all H files, sample code and}
- { projects, and documentation.}
-
- { I welcome any comments or suggestions that will help me improve or extend the}
- { functionality of the Task Manager. You can reach me at:}
-
- { Internet: Michael_Hecht@mac.sas.com}
- { AppleLink: SAS.HECHT}
-
- { Happy Tasking!}
-
- { --Michael Hecht}
-
- { Pascal Conversion by Peter N Lewis <peter@cujo.curtin.edu.au>, Aug 1992 }
-
- interface
-
- const
- { Globals }
- CurStackBaseA = $908;
- HeapEndA = $114;
- WindowListA = $9D6;
-
- { Define the following constant as true to include stack checking code }
- { Define the following constant as false to remove stack checking code }
- TASK_DEBUG = false;
-
- { Default options }
- kDefaultWakeTime = 60; { One second }
-
- type
- longPtr = ^longInt;
-
- { Task procedure }
- type
- TaskProcPtr = ProcPtr;
- { procedure TaskProc(taskRefCon:longInt) }
-
- procedure CallTaskProc (taskRefCon: longInt; p: TaskProcPtr);
- inline
- $205F, $4E90;
-
-
- { Routines }
- function InitTasking: OSErr;
- function TermTasking: OSErr;
-
- { Creating tasks }
- function NewTask (taskProc: TaskProcPtr; taskTermProc: TaskProcPtr; taskRefCon: univ longInt; var taskRefNum: integer): OSErr;
- function DisposeTask (taskRefNum: integer): OSErr;
-
- { Running tasks }
- function RunTasks (wakeTime: longInt): OSErr;
- function TaskYield: OSErr;
-
- { Getting task info }
- function CurrentTask: integer;
- function CountTasks: integer;
- function GetIndTask (index: integer): integer;
-
- { Task reference constant }
- function GetTaskRefCon (taskRefNum: integer): longInt;
- function SetTaskRefCon (taskRefNum: integer; taskRefCon: longInt): OSErr;
-
- implementation
-
- type
- jmp_buf = record { preserve D2 as well! }
- d2, d3, d4, d5, d6, d7, a1, a2, a3, a4, a6, a7: longInt;
- end;
-
- function setjmp (var regs: jmp_buf): integer;
- inline
- $205F, $7000, $43FA, $0006, $48D0, $DEFC, $3E80;
-
- procedure longjmp (var regs: jmp_buf; status: integer);
- inline
- $301F, $205F, $4CD8, $DEFC, $4ED1;
-
- function GetRegD2: Ptr;
- inline
- $2E82;
-
- procedure SetRegD2 (n: univ ptr);
- inline
- $241F;
-
- type
- TaskEnvironmentRecord = record
- envRegisters: jmp_buf;
- envStack: handle;
- end;
- TaskEnvironmentPtr = ^TaskEnvironmentRecord;
-
- { Register ordering within the jmp_buf }
- regs = (d3, d4, d5, d6, d7,{}
- a1, a2, a3, a4, a6, a7);
-
- TaskRecord = record
- taskProc: TaskProcPtr;
- taskTermProc: TaskProcPtr;
- taskRefCon: longInt;
- taskRefNum: integer;
- taskEnvironment: TaskEnvironmentRecord;
- taskFlags: longInt;
- end;
- TaskPtr = ^TaskRecord;
-
- { Values for gTaskMgrFlags }
- type
- TaskMgrFlags = set of (useTempMem, tasksRunning);
-
- { Return values from setjmp; negative values are OSErr's }
- const
- SJsaveEnvironment = 0;
- SJtaskResume = 1;
- SJtaskSuspend = 2;
-
- type
- TaskList = record
- numTasks: integer;
- theTask: array[0..100] of TaskRecord;
- end;
- TaskListPtr = ^TaskList;
- TaskListHandle = ^TaskListPtr;
-
- var
- gTaskList: TaskListHandle;
- gTaskAtHand: integer;
- gCurrentTask: TaskRecord;
- gNextTaskRefNum: integer;
- gTaskMgrFlags: TaskMgrFlags;
- gAppEnvironment: TaskEnvironmentRecord;
- gTimeToStop: longInt;
- CurStackBase: longInt;
-
- function SizeOfTaskList (n: integer): longInt;
- begin
- SizeOfTaskList := sizeof(integer) + n * sizeof(TaskRecord);
- end;
-
- function InitTasking: OSErr;
- var
- err: OSErr;
- response, tempMask: longInt;
- CurStackBaseP: longPtr;
- begin
- CurStackBaseP := longPtr(CurStackBaseA);
- CurStackBase := CurStackBaseP^;
-
- { Allocate the task list }
- gTaskList := TaskListHandle(NewHandle(SizeOfTaskList(0)));
- if gTaskList = nil then begin
- err := MemError;
- end
- else begin
-
- { Initialize global data }
- gTaskAtHand := -1; { Run task 0 first }
- gTaskList^^.numTasks := 0;
- gNextTaskRefNum := 1;
- gTaskMgrFlags := [];
-
- { Determine if temporary memory is available }
- if Gestalt(gestaltOSAttr, response) = noErr then begin
- if BTST(response, gestaltTempMemSupport) and BTST(response, gestaltRealTempMemory) and BTST(response, gestaltTempMemTracked) then
- gTaskMgrFlags := gTaskMgrFlags + [useTempMem];
- end;
- err := noErr;
- end;
-
- InitTasking := err;
- end;
-
- function TermTasking: OSErr;
- var
- err: OSErr;
- taskIndex: integer;
- begin
-
- { Can't terminate from a task }
- if tasksRunning in gTaskMgrFlags then begin
- err := paramErr;
- end
- else begin
- { * Kill all tasks. We do this from back to front because it's more}
- { * efficient, for two reasons:}
- { *}
- { * • Less memory gets moved when we shrink the task list.}
- { *}
- { * • The taskIndexes are looked up much faster when starting at}
- { * the end of the list.}
-
- err := noErr;
- for taskIndex := gTaskList^^.numTasks - 1 downto 0 do begin
-
- err := DisposeTask(gTaskList^^.theTask[taskIndex].taskRefNum);
- if err <> noErr then
- leave;
- end;
-
- { Dispose of the task list }
- DisposHandle(Handle(gTaskList));
- gTaskList := nil;
- end;
- TermTasking := err;
- end;
-
- function CountTasks: integer;
- begin
- CountTasks := gTaskList^^.numTasks;
- end;
-
- function CurrentTask: integer;
- begin
- if tasksRunning in gTaskMgrFlags then
- CurrentTask := gCurrentTask.taskRefNum
- else
- CurrentTask := 0;
- end;
-
- function GetIndTask (index: integer): integer;
- begin
- if (index < 0) or (index >= gTaskList^^.numTasks) then
- GetIndTask := 0
- else
- GetIndTask := gTaskList^^.theTask[index].taskRefNum;
- end;
-
- function GetTaskIndex (taskRefNum: integer): integer;
- var
- taskIndex: integer;
- begin
-
- { * Since taskRefNums start at 1 and always increase, we can assume that}
- { * the taskIndex will always be less than the taskRefNum or the number of}
- { * tasks, which ever is smallest. This makes a good starting point in our}
- { * search for the task.}
-
-
- taskIndex := gTaskList^^.numTasks;
- if taskRefNum < taskIndex then
- taskIndex := taskRefNum;
- taskIndex := taskIndex - 1;
-
- while taskIndex >= 0 do begin
- if gTaskList^^.theTask[taskIndex].taskRefNum = taskRefNum then
- leave;
- taskIndex := taskIndex - 1;
- end;
-
- { Note that a negative value will be returned if the task isn't found }
- GetTaskIndex := taskIndex;
- end;
-
- function GetTaskRefCon (taskRefNum: integer): longInt;
- var
- taskIndex: integer;
- begin
- taskIndex := GetTaskIndex(taskRefNum);
- if taskIndex < 0 then begin
- GetTaskRefCon := 0;
- end
- else begin
- GetTaskRefCon := gTaskList^^.theTask[taskIndex].taskRefCon;
- end;
- end;
-
- function SetTaskRefCon (taskRefNum: integer; taskRefCon: longInt): OSErr;
- var
- taskIndex: integer;
- begin
- taskIndex := GetTaskIndex(taskRefNum);
- if taskIndex < 0 then begin
- SetTaskRefCon := paramErr;
- end
- else begin
- gTaskList^^.theTask[taskIndex].taskRefCon := taskRefCon;
- SetTaskRefCon := noErr;
- end;
- end;
-
- function AllocStack (var theEnvironment: TaskEnvironmentRecord; stackSize: Size): OSErr;
- var
- err: OSErr;
- begin
- { Has the stack already been allocated? }
- if theEnvironment.envStack <> nil then begin
-
- { Try to reallocate it }
- ReallocHandle(theEnvironment.envStack, stackSize);
- if MemError = noErr then begin
- AllocStack := noErr;
- exit(AllocStack);
- end;
-
- { Dispose of the stack and try allocating a whole new one }
- DisposHandle(theEnvironment.envStack);
- end;
-
- { Try temporary memory first }
- if useTempMem in gTaskMgrFlags then begin
-
- theEnvironment.envStack := TempNewHandle(stackSize, err);
- if err = noErr then begin
- AllocStack := noErr;
- exit(AllocStack);
- end;
- end;
-
- { If that didn't work, try allocating from the heap }
- theEnvironment.envStack := NewHandle(stackSize);
- AllocStack := MemError;
- end;
-
- function SaveEnvironment (theEnvironment: TaskEnvironmentPtr): OSErr;
- var
- err: OSErr;
- status: OSErr;
- stackPtr: Ptr;
- stackSize: Size;
- begin
- { preserve the environment ptr across the stack switch }
- {$PUSH}
- {$D-}
- SetRegD2(theEnvironment);
- { Save the registers }
- status := setjmp(theEnvironment^.envRegisters);
- theEnvironment := TaskEnvironmentPtr(GetRegD2);
- {$POP}
- if status <> SJsaveEnvironment then begin
-
- { Restore the stack }
- stackSize := GetHandleSize(theEnvironment^.envStack);
- BlockMove(theEnvironment^.envStack^, ptr(CurStackBase - stackSize), stackSize);
- HPurge(theEnvironment^.envStack);
- err := status;
- end
- else begin
-
- { Allocate the stack }
- stackPtr := Ptr(theEnvironment^.envRegisters.a7);
- stackSize := CurStackBase - ord(stackPtr);
- err := AllocStack(theEnvironment^, stackSize);
- if err = noErr then begin
- { Save the stack }
- BlockMove(stackPtr, theEnvironment^.envStack^, stackSize);
- end;
- end;
- SaveEnvironment := err;
- end;
-
- procedure RestoreEnvironment (theEnvironment: TaskEnvironmentPtr; status: OSErr);
- var
- HeapEndP: longPtr;
- peek: WindowPeek;
- msg, title: Str255;
- WindowListP: ^windowPeek;
- begin
- HeapEndP := longPtr(HeapEndA);
- { Can't let the stack cross into the heap! }
- if theEnvironment^.envRegisters.a7 < HeapEndP^ then begin
- if TASK_DEBUG then begin
- DebugStr('TaskMgr: Stack overflow');
- ExitToShell;
- end
- else begin
- SysError(28);
- end;
- end;
-
- if TASK_DEBUG then begin
- { Look for windows in the stack and warn about them }
- WindowListP := POINTER(WindowListA);
- peek := WindowListP^;
- while peek <> nil do begin
- if ord(peek) >= HeapEndP^ then begin
- GetWTitle(windowPtr(peek), title);
- DebugStr(concat('TaskMgr: Window in stack: ', title));
- ExitToShell;
- end;
- peek := peek^.nextWindow;
- end;
- end;
-
- { Restore the registers }
- longjmp(theEnvironment^.envRegisters, status);
- end;
-
- procedure StartNextTask;
- begin
- { Move to next task at hand }
- gTaskAtHand := gTaskAtHand + 1;
- if gTaskAtHand >= gTaskList^^.numTasks then
- gTaskAtHand := 0;
-
- { Keep gCurrentTask up-to-date }
- gCurrentTask := gTaskList^^.theTask[gTaskAtHand];
-
- { Start the next task }
- RestoreEnvironment(@gCurrentTask.taskEnvironment, SJtaskResume);
-
- { This statement should never be hit }
- DebugStr('TaskMgr/StartNextTask: returned from RestoreEnvironment!?!?');
- end;
-
-
- function RunTasks (wakeTime: longInt): OSErr;
- var
- status: OSErr;
- begin
-
- if TASK_DEBUG then begin
- { Called from task? }
- if tasksRunning in gTaskMgrFlags then begin
- DebugStr('TaskMgr/RunTasks: Called from task');
- RunTasks := paramErr;
- exit(RunTasks);
- end;
- end;
-
- { Nothing to do if no tasks to run }
- if gTaskList^^.numTasks = 0 then begin
- RunTasks := noErr;
- exit(RunTasks);
- end;
-
- { Determine when to stop running tasks }
- gTimeToStop := TickCount;
- gTimeToStop := gTimeToStop + wakeTime;
-
- { Save application's state }
- status := SaveEnvironment(@gAppEnvironment);
- case status of
- SJsaveEnvironment: begin
- { We just saved the application's environment; time to start next task }
- StartNextTask;
- { StartNextTask never returns }
- end;
-
- SJtaskSuspend: begin
- { Tasks have suspended execution; time to return to the application }
- status := noErr;
- end;
- otherwise begin
- { Anything else is an OSErr code }
-
- { This case will be hit if SaveEnvironment couldn't }
- if TASK_DEBUG then
- DebugStr('TaskMgr/RunTasks: Can''t save environment');
- end;
- end;
-
- RunTasks := status;
- end;
-
- function TaskYield: OSErr;
- var
- taskAtHand: integer;
- status: OSErr;
- timeToSuspend: Boolean;
- theEvent: EventRecord;
- begin
-
- if TASK_DEBUG then begin
- { Called from application? }
- if not (tasksRunning in gTaskMgrFlags) then begin
- DebugStr('TaskMgr/TaskYield: Called from application');
- TaskYield := paramErr;
- exit(TaskYield);
- end;
- end;
-
- { * Determine if it's time to return to the application.}
- { *}
- { * It's that time if the wake time has run out or if the application}
- { * received an event.}
-
- timeToSuspend := (TickCount >= gTimeToStop) or EventAvail(everyEvent, theEvent);
-
- { if it's not time to suspend and I'm the only task, then I'll just keep running }
- if not timeToSuspend and (gTaskList^^.numTasks = 1) then begin
- TaskYield := noErr;
- exit(TaskYield);
- end;
-
- { Save the current task's environment }
- status := SaveEnvironment(@gCurrentTask.taskEnvironment);
-
- { Return to the task }
- if status <> SJsaveEnvironment then begin
-
- if status > noErr then begin
- status := noErr
-
- { A negative status is an OSErr }
- end
- else if TASK_DEBUG then begin
- DebugStr('TaskMgr/TaskYield: Can''t save environment');
- end;
-
- { Tasks are running now }
- gTaskMgrFlags := gTaskMgrFlags + [tasksRunning];
-
- TaskYield := status;
- exit(TaskYield);
- end;
-
- { Put the saved environment in the task list }
- gTaskList^^.theTask[gTaskAtHand].taskEnvironment := gCurrentTask.taskEnvironment;
-
- { If it's time to return to the application, then do so }
- if timeToSuspend then begin
-
- { Tasks no longer running }
- gTaskMgrFlags := gTaskMgrFlags - [tasksRunning];
-
- { Return to the application }
- RestoreEnvironment(@gAppEnvironment, SJtaskSuspend);
- end;
-
- { Start the next task }
- StartNextTask;
- end;
-
- function DisposeTask (taskRefNum: integer): OSErr;
- var
- taskIndex: integer;
- dyingTask: TaskRecord;
- harakiri: Boolean;
- dummy: longInt;
- begin
-
- taskIndex := GetTaskIndex(taskRefNum);
- if taskIndex < 0 then begin
- DisposeTask := paramErr;
- exit(DisposeTask);
- end;
-
- { Are we deleting the current task? }
- harakiri := (tasksRunning in gTaskMgrFlags) and (taskIndex = gTaskAtHand);
-
- { Point to the task record of the task we're disposing }
- dyingTask := gTaskList^^.theTask[taskIndex];
-
- { If the task has a term proc, call it now }
- if dyingTask.taskTermProc <> nil then
- CallTaskProc(dyingTask.taskRefCon, dyingTask.taskTermProc);
-
- { We can dispose of its stack now }
- DisposHandle(dyingTask.taskEnvironment.envStack);
-
- { Remove the task from the task list }
- dummy := Munger(Handle(gTaskList), SizeOfTaskList(taskIndex), nil, sizeof(TaskRecord), @gTaskList, 0);
-
- { Fix up task at hand if necessary }
- if gTaskAtHand >= taskIndex then begin
- gTaskAtHand := gTaskAtHand - 1;
- if gTaskAtHand < 0 then begin
- gTaskAtHand := gTaskList^^.numTasks;
- end;
- end;
-
- { One less task to keep track of }
- gTaskList^^.numTasks := gTaskList^^.numTasks - 1;
-
- { Return to the application if we deleted ourselves }
- if harakiri then begin
-
- { Tasks are no longer running (we will be returning to the application) }
- gTaskMgrFlags := gTaskMgrFlags - [tasksRunning];
- RestoreEnvironment(@gAppEnvironment, SJtaskSuspend);
- end;
-
- { Not disposing of ourselves; return to the caller }
- DisposeTask := noErr;
- end;
-
- procedure TaskLife;
- var
- oe: OSErr;
- begin
- { TaskLife is the task's life cycle }
-
- { We are now running a task }
- gTaskMgrFlags := gTaskMgrFlags + [tasksRunning];
-
- { Call the task procedure }
- CallTaskProc(gCurrentTask.taskRefCon, gCurrentTask.taskProc);
-
- { Delete the task }
- oe := DisposeTask(gCurrentTask.taskRefNum);
- end;
-
- function NewTask (taskProc: TaskProcPtr; taskTermProc: TaskProcPtr; taskRefCon: univ longInt; var taskRefNum: integer): OSErr;
- var
- err: OSErr;
- status: OSErr;
- saveTask: TaskRecord;
- begin
-
- { Make a backup copy of the current task }
- saveTask := gCurrentTask;
-
- { Initialize the task record }
- gCurrentTask.taskProc := taskProc;
- gCurrentTask.taskTermProc := taskTermProc;
- gCurrentTask.taskRefCon := taskRefCon;
- gNextTaskRefNum := gNextTaskRefNum + 1;
- gCurrentTask.taskRefNum := gNextTaskRefNum;
- gCurrentTask.taskFlags := 0;
- gCurrentTask.taskEnvironment.envStack := nil;
-
- { Give task refNum back to caller }
- taskRefNum := gCurrentTask.taskRefNum;
-
- { Initialize the task's environment }
- status := SaveEnvironment(@gCurrentTask.taskEnvironment);
- if status < noErr then begin
- err := status;
- end
- else begin
-
- if status > SJsaveEnvironment then begin
- TaskLife;
- { Never to return… }
- end;
-
- { Add task to task list }
- err := PtrAndHand(@gCurrentTask, Handle(gTaskList), sizeof(TaskRecord));
- if err <> noErr then begin
-
- { Dispose of the stack }
- DisposHandle(gCurrentTask.taskEnvironment.envStack);
-
- { Not enough memory to add it to the task list }
- end
- else begin
- gTaskList^^.numTasks := gTaskList^^.numTasks + 1;
-
- { All dressed up and nowhere to go }
- err := noErr;
- end;
- end;
-
- gCurrentTask := saveTask;
- NewTask := err;
- end;
-
- end.